package gamelogic

import (
	"server_cluster/common/config"
	"server_cluster/common/gdconf"
	"server_cluster/common/logger"
	"server_cluster/common/mysock"
	"server_cluster/common/protoc/pb"
	"server_cluster/common/tools"
	"server_cluster/logic/sharemem"
)

func AddItems(items map[int32]int32, user *sharemem.User) int32 {
	pbUserItem := new(pb.User_Item)
	remainItems := make(map[int32]int32)
	var pbUserInfo *pb.User_Info = nil
	if len(items) == 0 {
		return config.SERVER_ERROR
	}
	for itemId, itemCount := range items {
		itemConfig := gdconf.GetItemConfig(itemId)
		if itemConfig == nil {
			continue
		}
		switch itemConfig.Type {
		case gdconf.ITEM_TYPE_CURRENCY: // 货币类
			ChangeUserRes(itemId, itemCount, user)
			pbUserInfo = user.PacketUserInfo()
		case gdconf.ITEM_TYPE_EQUIP: // 装备
			for i := 0; i < int(itemCount); i++ {
				ok := AddEquip(itemId, user)
				if !ok {
					remainItems[itemId]++
					continue
				}
			}
		case gdconf.ITEM_TYPE_EQUIP_JEWEL: // 装备宝石
			for i := 0; i < int(itemCount); i++ {
				ok := AddEquipJewel(itemId, user)
				if !ok {
					remainItems[itemId]++
					continue
				}
			}
		case gdconf.ITEM_TYPE_WEAPON: // 武器
			for i := 0; i < int(itemCount); i++ {
				ok := AddWeapon(itemId, user)
				if !ok {
					remainItems[itemId]++
					continue
				}
			}
			user.SetSync()
		case gdconf.ITEM_TYPE_SPECIAL: // 特殊道具（没有 时效性道具）
			key := sharemem.InitItemKeyById(itemId)
			index, ok := user.GetItemSpecialMap()[key]
			if ok {
				user.Item.Special[index].UpdateItemNum(itemConfig, itemCount)
			} else {
				var i int16
				for i = 0; i < sharemem.USER_ITEM_SPECIAL; i++ {
					if !user.Item.Special[i].IsUse() {
						user.Item.Special[i].CreateItemSpecial(itemConfig, itemCount)
						// 添加索引
						index = i
						user.SetItemSpecialMap(key, index)
						break
					}
				}
				if i >= sharemem.USER_ITEM_SPECIAL {
					remainItems[itemId] = itemCount
					break
				}
			}
			tmp := &pb.Item_Base{
				Cell:      int32(index),
				Id:        user.Item.Special[index].GetId(),
				Type:      int32(user.Item.Special[index].Type),
				Num:       user.Item.Special[index].Num,
				CdEndTime: user.Item.Special[index].CdEndTime,
				EndTime:   user.Item.Special[index].EndTime,
			}
			pbUserItem.Special = append(pbUserItem.Special, tmp)
		default: // 可主动使用道具+材料
			var day int8
			day = 0
			key := sharemem.InitItemKeyById(itemId)
			time_now := sharemem.MyShm.TimeNow
			if itemConfig.ExTimeType == gdconf.ITEM_EX_TYPE_1 {
				day = int8(tools.Timestamp2time(time_now).Day() % sharemem.ITEM_EXPIRE_RANGE)
				key += int32(day)
			}
			index, ok := user.GetItemNormalMap()[key]
			if ok {
				user.Item.Normal[index].UpdateItemNum(itemConfig, itemCount)
			} else {
				var i int16
				for i = 0; i < sharemem.USER_ITEM_NORMAL; i++ {
					if !user.Item.Normal[i].IsUse() {
						user.Item.Normal[i].CreateItemNormal(itemConfig, itemCount, day, time_now)
						// 添加索引
						index = i
						user.SetItemNormalMap(key, index)
						break
					}
				}
				if i >= sharemem.USER_ITEM_NORMAL {
					remainItems[itemId] = itemCount
					break
				}
			}
			tmp := &pb.Item_Base{
				Cell:      int32(index),
				Id:        user.Item.Normal[index].GetId(),
				Type:      int32(user.Item.Normal[index].Type),
				Num:       user.Item.Normal[index].Num,
				CdEndTime: user.Item.Normal[index].CdEndTime,
				EndTime:   user.Item.Normal[index].EndTime,
			}
			pbUserItem.Normal = append(pbUserItem.Normal, tmp)
		}
	}
	if len(remainItems) != 0 {
		var mailConfig = gdconf.GetMailConfig(1009)
		if mailConfig != nil {
			AddUserMail(mailConfig.Title, mailConfig.Content, mailConfig.Sender, 0, mailConfig.Importance, remainItems, user)
		}
	}
	if len(pbUserItem.Normal) != 0 || len(pbUserItem.Special) != 0 {
		SendMsg(config.SC_ItemChangeNtf, &pb.SC_ItemChangeNtf{Item: pbUserItem}, nil, user)
	}
	if pbUserInfo != nil {
		SendMsg(config.SC_ResChangeNtf, &pb.SC_ResChangeNtf{Info: pbUserInfo}, nil, user)
	}
	TriggerQuest(user, sharemem.QUEST_FINISH_COND_TYPE_HAVE_ITEM, true)
	for itemId := range items {
		UnlockCodex(pb.CodexType_CODEX_WEAPON, itemId, user)
		UnlockCodex(pb.CodexType_CODEX_EQUIP, itemId, user)
		UnlockCodex(pb.CodexType_CODEX_FISH, itemId, user)
	}
	for itemId, itemCount := range items {
		itemConfig := gdconf.GetItemConfig(itemId)
		if itemConfig == nil {
			continue
		}
		if itemConfig.AutoUse {
			useItem := AutoFoundItem(map[int32]int32{itemId: itemCount}, user)
			if useItem == nil {
				logger.Error("auto found item error, itemId: %v, itemCount: %v, uid: %v", itemId, itemCount, user.GetUid())
				continue
			}
			ret, _ := UseItem(useItem, user)
			if ret != config.OK {
				logger.Error("use item error, ret: %v, uid: %v", ret, user.GetUid())
				continue
			}
		}
	}
	return config.OK
}

// CheckSubItems 检查道具数量是否合法
func CheckSubItems(items []*pb.Item_Req, user *sharemem.User) int32 {
	for i := 0; i < len(items); i++ {
		if items[i].Num <= 0 {
			return config.ITEM_NOT_ENOUGH
		}
		switch items[i].Type {
		case gdconf.ITEM_TYPE_CURRENCY:
			if user.CheckUserRes(items[i].Id, items[i].Num) != config.OK {
				return config.ITEM_NOT_ENOUGH
			}
		case gdconf.ITEM_TYPE_SPECIAL:
			if items[i].Cell < 0 || items[i].Cell >= sharemem.USER_ITEM_SPECIAL {
				return config.SERVER_ERROR
			}
			// 校验id
			if !user.Item.Special[items[i].Cell].IsMe(items[i].Id) {
				delete(user.GetItemSpecialMap(), user.Item.Special[items[i].Cell].GetItemKey())
				return config.SERVER_ERROR
			}
			if !user.Item.Special[items[i].Cell].CheckItemNum(items[i].Num) {
				return config.ITEM_NOT_ENOUGH
			}
		default:
			if items[i].Cell < 0 || items[i].Cell >= sharemem.USER_ITEM_NORMAL {
				return config.SERVER_ERROR
			}
			if !user.Item.Normal[items[i].Cell].IsMe(items[i].Id) {
				delete(user.GetItemNormalMap(), user.Item.Normal[items[i].Cell].GetItemKey())
				return config.SERVER_ERROR
			}
			if !user.Item.Normal[items[i].Cell].CheckItemNum(items[i].Num) {
				return config.ITEM_NOT_ENOUGH
			}
		}
	}
	return config.OK
}

func SubItems(items []*pb.Item_Req, user *sharemem.User) {
	pbUserItem := new(pb.User_Item)
	var pbUserInfo *pb.User_Info = nil
	total := len(items)
	for i := 0; i < total; i++ {
		// num参数手动设置为负数
		switch items[i].Type {
		case gdconf.ITEM_TYPE_CURRENCY:
			ChangeUserRes(items[i].Id, -items[i].Num, user)
			pbUserInfo = user.PacketUserInfo()
		case gdconf.ITEM_TYPE_EQUIP:
			logger.Error("can not cost equip as item, itemReq: %v", items[i])
			continue
		case gdconf.ITEM_TYPE_EQUIP_JEWEL:
			logger.Error("can not cost equip jewel as item, itemReq: %v", items[i])
			continue
		case gdconf.ITEM_TYPE_WEAPON:
			logger.Error("can not cost weapon as item, itemReq: %v", items[i])
			continue
		case gdconf.ITEM_TYPE_SPECIAL:
			user.Item.Special[items[i].Cell].UpdateItemNum(nil, -items[i].Num)
			tmp := &pb.Item_Base{
				Cell:      items[i].Cell,
				Id:        user.Item.Special[items[i].Cell].GetId(),
				Type:      int32(user.Item.Special[items[i].Cell].Type),
				Num:       user.Item.Special[items[i].Cell].Num,
				CdEndTime: user.Item.Special[items[i].Cell].CdEndTime,
				EndTime:   user.Item.Special[items[i].Cell].EndTime,
			}
			pbUserItem.Special = append(pbUserItem.Special, tmp)
			// 检查清理格子
			user.CheckClearItem(user.Item.Special[items[i].Cell].Type, items[i].Cell)
		default:
			user.Item.Normal[items[i].Cell].UpdateItemNum(nil, -items[i].Num)
			tmp := &pb.Item_Base{
				Cell:      items[i].Cell,
				Id:        user.Item.Normal[items[i].Cell].GetId(),
				Type:      int32(user.Item.Normal[items[i].Cell].Type),
				Num:       user.Item.Normal[items[i].Cell].Num,
				CdEndTime: user.Item.Normal[items[i].Cell].CdEndTime,
				EndTime:   user.Item.Normal[items[i].Cell].EndTime,
			}
			pbUserItem.Normal = append(pbUserItem.Normal, tmp)
			// 检查清理格子
			user.CheckClearItem(user.Item.Normal[items[i].Cell].Type, items[i].Cell)
		}
	}
	if len(pbUserItem.Normal) != 0 || len(pbUserItem.Special) != 0 {
		SendMsg(config.SC_ItemChangeNtf, &pb.SC_ItemChangeNtf{Item: pbUserItem}, nil, user)
	}
	if pbUserInfo != nil {
		SendMsg(config.SC_ResChangeNtf, &pb.SC_ResChangeNtf{Info: pbUserInfo}, nil, user)
	}
}

func ComposeItem(itemId int32, user *sharemem.User) bool {
	itemComposeConfig := gdconf.GetItemComposeConfig(itemId)
	if itemComposeConfig == nil {
		return false
	}
	costItemMap := make(map[int32]int32)
	for _, item := range itemComposeConfig.CostItemList {
		costItemMap[item.ItemId] += item.ItemCount
	}
	costItemMap[gdconf.ITEM_GOLD] += itemComposeConfig.CostGold
	costItemList := AutoFoundItem(costItemMap, user)
	if costItemList == nil {
		logger.Error("auto found item error, costItemMap: %v, uid: %v", costItemMap, user.GetUid())
		return false
	}
	ret := CheckSubItems(costItemList, user)
	if ret != config.OK {
		logger.Error("check item error, ret: %v", ret)
		return false
	}
	SubItems(costItemList, user)
	AddItems(map[int32]int32{itemComposeConfig.ItemId: 1}, user)
	return true
}

func DecomposeItem(itemList []*pb.Item_Req, user *sharemem.User) (int32, map[int32]int32) {
	ret := CheckSubItems(itemList, user)
	if ret != config.OK {
		logger.Error("check item error, ret: %v", ret)
		return config.ITEM_NOT_ENOUGH, nil
	}
	SubItems(itemList, user)
	addItemMap := make(map[int32]int32)
	for _, itemReq := range itemList {
		itemResolveConfig := gdconf.GetItemResolveConfig(itemReq.Id)
		if itemResolveConfig == nil {
			continue
		}
		for _, item := range itemResolveConfig.ItemList {
			addItemMap[item.ItemId] += item.ItemCount * itemReq.Num
		}
	}
	AddItems(addItemMap, user)
	return config.OK, addItemMap
}

func UseItem(itemList []*pb.Item_Req, user *sharemem.User) (int32, map[int32]int32) {
	ret := CheckSubItems(itemList, user)
	if ret != config.OK {
		logger.Error("check item error, ret: %v", ret)
		return config.ITEM_NOT_ENOUGH, nil
	}
	itemMap := make(map[int32]int32)
	for _, itemReq := range itemList {
		itemActiveConfig := gdconf.GetItemUseConfig(itemReq.Id)
		if itemActiveConfig == nil {
			continue
		}
		now := sharemem.MyShm.TimeNow
		item := &(user.Item.Normal[itemReq.Cell])
		if item.CdEndTime > now {
			logger.Error("item is in cd time, itemId: %v, cdEndTime: %v, uid: %v", item.GetId(), item.CdEndTime, user.GetUid())
			continue
		}
		if itemActiveConfig.CdTime != 0 {
			item.CdEndTime = now + int64(itemActiveConfig.CdTime)
			item.SetSync()
		}
		for i := 0; i < int(itemReq.Num); i++ {
			switch itemActiveConfig.Param[0] {
			case gdconf.ItemUseTypeReward:
				ret, subItemMap, _ := RewardItem(itemActiveConfig.Param[1], nil, user)
				if ret != config.OK {
					logger.Error("reward item error, ret: %v, rewardId: %v, uid: %v", ret, itemActiveConfig.Param, user.GetUid())
				}
				for itemId, itemCount := range subItemMap {
					itemMap[itemId] += itemCount
				}
			case gdconf.ItemUseTypeAddServerBuff:
				logger.Error("item use type not impl, type: %v, uid: %v", itemActiveConfig.Param[0], user.GetUid())
			case gdconf.ItemUseTypeAddGlobalExp:
				logger.Error("item use type not impl, type: %v, uid: %v", itemActiveConfig.Param[0], user.GetUid())
			case gdconf.ItemUseTypeAddRole:
				AddUserRole(itemActiveConfig.Param[1], user)
			case gdconf.ItemUseTypeUnlockHomeBuilding:
				user.AddUnlockBuilding(itemActiveConfig.Param[1])
			default:
				logger.Error("item use type unsupported, type: %v, uid: %v", itemActiveConfig.Param[0], user.GetUid())
			}
		}
	}
	SubItems(itemList, user)
	return config.OK, itemMap
}

func RewardItem(rewardId int32, roleList []int32, user *sharemem.User) (int32, map[int32]int32, int32) {
	rewardConfig := gdconf.GetConfigReward(rewardId)
	if rewardConfig == nil {
		return config.CONFIG_NOT_FOUND, nil, 0
	}
	rewardMap := rewardConfig.GetRewards(1, nil, roleList)
	if rewardMap == nil {
		return config.CONFIG_NOT_FOUND, nil, 0
	}
	ret := AddItems(rewardMap, user)
	return ret, rewardMap, rewardConfig.EXP
}

func AutoFoundItem(itemMap map[int32]int32, user *sharemem.User) []*pb.Item_Req {
	itemReq := make([]*pb.Item_Req, 0)
	for itemId, itemCount := range itemMap {
		itemConfig := gdconf.GetItemConfig(itemId)
		if itemConfig == nil {
			continue
		}
		cell := -1
		for index, item := range user.Item.Normal {
			if item.Id.NewVal == itemId {
				cell = index
				break
			}
		}
		if cell == -1 {
			for index, item := range user.Item.Special {
				if item.Id.NewVal == itemId {
					cell = index
					break
				}
			}
		}
		itemReq = append(itemReq, &pb.Item_Req{
			Cell: int32(cell),
			Id:   itemId,
			Type: itemConfig.Type,
			Num:  itemCount,
		})
	}
	ret := CheckSubItems(itemReq, user)
	if ret != config.OK {
		logger.Error("check item error, ret: %v, uid: %v", ret, user.GetUid())
		return nil
	}
	return itemReq
}

func CostItems(itemMap map[int32]int32, user *sharemem.User) bool {
	if len(itemMap) == 0 {
		return true
	}
	for id, count := range itemMap {
		if count == 0 {
			delete(itemMap, id)
		}
	}
	itemReq := AutoFoundItem(itemMap, user)
	if itemReq == nil {
		logger.Error("cost item error, itemMap: %v, uid: %v", itemMap, user.GetUid())
		return false
	}
	SubItems(itemReq, user)
	return true
}

func CheckBags(itemNum4Type map[int32]int32, user *sharemem.User) int32 {
	for k, v := range itemNum4Type {
		switch k {
		case gdconf.ITEM_TYPE_EQUIP_JEWEL:
			if v > user.GetEquipJewelRemainCellNum() {
				return config.STORE_CAP_MAX
			}
		case gdconf.ITEM_TYPE_WEAPON:
			if v > user.GetWeaponRemainCellNum() {
				return config.STORE_CAP_MAX
			}
		case gdconf.ITEM_TYPE_EQUIP:
			if v > user.GetEquipRemainCellNum() {
				return config.STORE_CAP_MAX
			}
			//case gdconf.ITEM_TYPE_SPECIAL:
			//	if v > user.GetRemainItemSpecialCell() {
			//		return config.STORE_CAP_MAX
			//	}
			//default:
			//	if v > user.GetRemainItemNormalCell() {
			//		return config.STORE_CAP_MAX
			//	}
		}
	}
	return config.OK
}

//==============消息协议处理============

func ItemActionReq(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.CS_ItemActionReq, new(pb.CS_ItemActionReq), buf, user).(*pb.CS_ItemActionReq)
	if !ok {
		return config.PROTO_ERROR
	}
	rsp := &pb.SC_ItemActionRsp{
		RetCode:  config.OK,
		Type:     req.Type,
		ItemList: make([]*pb.Item, 0),
	}
	switch req.Type {
	case config.ItemActionTypeUse:
		code, itemMap := UseItem(req.ItemList, user)
		if code != config.OK {
			return SendMsg(config.SC_ItemActionRsp, &pb.SC_ItemActionRsp{RetCode: code}, buf, user)
		}
		for itemId, itemCount := range itemMap {
			rsp.ItemList = append(rsp.ItemList, &pb.Item{Id: itemId, Num: itemCount})
		}
	case config.ItemActionTypeDec:
		if len(req.ItemList) != 0 {
			// 分解道具
			code, itemMap := DecomposeItem(req.ItemList, user)
			if code != config.OK {
				return SendMsg(config.SC_ItemActionRsp, &pb.SC_ItemActionRsp{RetCode: code}, buf, user)
			}
			for itemId, itemCount := range itemMap {
				rsp.ItemList = append(rsp.ItemList, &pb.Item{Id: itemId, Num: itemCount})
			}
		}
		if len(req.EquipKeyList) != 0 || len(req.JewelKeyList) != 0 {
			itemIdList := make([]int32, 0)
			if len(req.EquipKeyList) != 0 {
				// 分解装备
				deleteEquipKeyList := make([]int32, 0)
				for _, equipKey := range req.EquipKeyList {
					equip := user.GetEquip4Key(equipKey)
					if equip == nil {
						logger.Error("equip not exist, equipKey: %v, uid: %v", equipKey, user.GetUid())
						return SendMsg(config.SC_ItemActionRsp, &pb.SC_ItemActionRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
					}
					itemIdList = append(itemIdList, equip.GetId())
					costEquipKey, ok := CostEquip(equip, user)
					if !ok {
						logger.Error("cost equip error, equipId: %v, uid: %v", equip.GetId(), user.GetUid())
						continue
					}
					deleteEquipKeyList = append(deleteEquipKeyList, costEquipKey)
				}
				SendMsg(config.SC_EquipDeleteNtf, &pb.SC_EquipDeleteNtf{DeleteEquipKeyList: deleteEquipKeyList}, buf, user)
			}
			if len(req.JewelKeyList) != 0 {
				// 分解宝石
				deleteJewelKeyList := make([]int32, 0)
				for _, jewelKey := range req.JewelKeyList {
					jewel := user.GetEquipJewel4Key(jewelKey)
					if jewel == nil {
						logger.Error("equip jewel not exist, jewelKey: %v, uid: %v", jewelKey, user.GetUid())
						return SendMsg(config.SC_ItemActionRsp, &pb.SC_ItemActionRsp{RetCode: config.OBJECT_NOT_FOUND}, buf, user)
					}
					itemIdList = append(itemIdList, jewel.GetId())
					costEquipJewelKey, ok := CostEquipJewel(jewel, user)
					if !ok {
						logger.Error("cost equip jewel error, jewelId: %v, uid: %v", jewel.GetId(), user.GetUid())
						continue
					}
					deleteJewelKeyList = append(deleteJewelKeyList, costEquipJewelKey)
				}
				SendMsg(config.SC_EquipDeleteNtf, &pb.SC_EquipDeleteNtf{DeleteJewelKeyList: deleteJewelKeyList}, buf, user)
			}
			addItemMap := make(map[int32]int32)
			for _, itemId := range itemIdList {
				itemResolveConfig := gdconf.GetItemResolveConfig(itemId)
				if itemResolveConfig == nil {
					return SendMsg(config.SC_ItemActionRsp, &pb.SC_ItemActionRsp{RetCode: config.CONFIG_NOT_FOUND}, buf, user)
				}
				for _, item := range itemResolveConfig.ItemList {
					addItemMap[item.ItemId] += item.ItemCount
					rsp.ItemList = append(rsp.ItemList, &pb.Item{Id: item.ItemId, Num: item.ItemCount})
				}
			}
			AddItems(addItemMap, user)
		}
	}
	return SendMsg(config.SC_ItemActionRsp, rsp, buf, user)
}
